// Object.defineProperty 只能对对象的某一个属性进行劫持

import { ArrayMethods } from "./arr";
import Dep from "./dep";

export function observer(data) {
  if (typeof data !== "object" || data === null) {
    return data;
  }
  // 对象、数组继续下面操作
  return new Observer(data);
}

class Observer {
  constructor(value) {
    this.dep = new Dep(); // 给所有的对象类型都添加一个dep
    // 自己定义了一个不可枚举的属性，下面this指向 Observer类的实例对象
    Object.defineProperty(value, "__ob__", {
      enumerable: false,
      configurable: false,
      value: this,
    });
    if (Array.isArray(value)) {
      // 修改数组的隐式原型，重写数组方法，原型链，那么数组再使用这些方法就是调用ArrayMethods里面的方法
      value.__proto__ = ArrayMethods;
      // 对数组进行遍历监听，相当于迭代
      this.observerArray(value);
    } else {
      // 遍历对象属性，进行 Object.defineProperty 劫持
      this.walk(value);
    }
  }
  walk(data) {
    Object.keys(data).forEach(key => {
      // 设置响应式
      defineReactive(data, key, data[key]);
    })
  }
  observerArray(ary) {
    ary.forEach(item => {
      observer(item);
    })
  }
}

function defineReactive(data, key, value) {
  let childDep = observer(value); // 深度监听，不满足条件就会跳出
  let dep = new Dep();
  Object.defineProperty(data, key, {
    get() {
      // 收集依赖
      // 初次渲染调用updateComponent之前，也即渲染函数render执行之前，pushTarget就已经收集了watcher
      // 所以这个Dep.target应该存在
      if (Dep.target) {
        dep.depend(); // 将Dep.target推入subs数组
        if (childDep && childDep.dep) {
          childDep.dep.depend();
        }
      }
      // console.log("获取数据get", value);
      return value;
    },
    set(newVal) {
      // console.log("设置数据set", newVal);
      if (newVal === value) return;
      observer(newVal); // 对设置的值也要进行深度监听，否则对设置的值进行get、set无响应
      value = newVal;
      // 依赖修改，通知更新
      dep.notify(); // 实际上就是再执行一遍render函数，重新拿取新的变量值
    }
  });
}